package net.qiqbframework.messaging;


import net.qiqbframework.serialization.SerializedObject;
import net.qiqbframework.serialization.Serializer;

import javax.annotation.Nonnull;
import java.util.Map;
import java.util.Optional;

/**
 * 表示处理某种形式的请求消息的结果的消息
 *
 */
public interface ResultMessage<R> extends Message<R> {

    /**
     * 判断执行结果是否异常
     *
     * @return 当执行结果有异常时，返回true，否则返回false
     */
    boolean isExceptional();

    /**
     * 如果出现异常结果消息，则返回异常，如果成功执行，则返回空 Optional 消息
     *
     */
    Optional<Throwable> optionalExceptionResult();

    /**
     * 返回异常结果。如果 isExceptional() 返回 true，则调用此方法。
     */
    default Throwable exceptionResult() throws IllegalStateException {
        return optionalExceptionResult().orElseThrow(IllegalStateException::new);
    }

    /**
     * If the this message contains an exception result, returns the details provided in the exception, if available.
     * If this message does not carry an exception result, or the exception result doesn't provide any
     * application-specific details, an empty optional is returned.
     *
     * @param <D> The type of application-specific details expected
     * @return an optional containing application-specific error details, if present
     */
    default <D> Optional<D> exceptionDetails() {
        return optionalExceptionResult().flatMap(HandlerExecutionException::resolveDetails);
    }

    @Override
    default <S> SerializedObject<S> serializePayload(Serializer serializer, Class<S> expectedRepresentation) {
        if (isExceptional()) {
            return serializer.serialize(exceptionDetails().orElse(null), expectedRepresentation);
        }
        return serializer.serialize(getPayload(), expectedRepresentation);
    }

    /**
     * Serializes the exception result. Will create a {@link RemoteExceptionDescription} from the {@link Optional}
     * exception in this ResultMessage instead of serializing the original exception.
     *
     * @param serializer             the {@link Serializer} used to serialize the exception
     * @param expectedRepresentation a {@link Class} representing the expected format
     * @param <T>                    the generic type representing the expected format
     * @return the serialized exception as a {@link SerializedObject}
     */
    default <T> SerializedObject<T> serializeExceptionResult(Serializer serializer, Class<T> expectedRepresentation) {
        return serializer.serialize(
                optionalExceptionResult().map(RemoteExceptionDescription::describing).orElse(null),
                expectedRepresentation
        );
    }

}
